﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Syntezator_mowy.Extras.Exception;

namespace Merger.Wav
{
    class WavFmtSChunk
    {
        private byte[] subchunkId;
        private byte[] subchunkSize;
        private byte[] audioFormat;
        private byte[] numChannels;
        private byte[] sampleRate;
        private byte[] byteRate;
        private byte[] blockAlign;
        private byte[] bitsPerSample;
        private byte[] extraParams;

        public WavFmtSChunk()
        {
            ASCIIEncoding encoder = new ASCIIEncoding();
            this.subchunkId = new byte[4];
            this.subchunkId = encoder.GetBytes("fmt ");
            this.subchunkSize = new byte[4];
            this.audioFormat = new byte[2];
            this.numChannels = new byte[2];
            this.sampleRate = new byte[4];
            this.byteRate = new byte[4];
            this.blockAlign = new byte[2];
            this.bitsPerSample = new byte[2];
            this.extraParams = new byte[2];
        }

        public WavFmtSChunk(byte[] subchunkId, byte[] subchunkSize, byte[] audioFormat, byte[] numChannels,
            byte[] sampleRate, byte[] byteRate, byte[] blockAlign, byte[] bitsPerSample)
        {
            ASCIIEncoding encoder = new ASCIIEncoding();
            if (subchunkId.Length != 4)
                throw new WrongFormatException();
            else if (encoder.GetString(subchunkId) != "fmt ")
                throw new FmtSubChunkFormatException();
            else
                this.subchunkId = subchunkId;

            if (subchunkSize.Length != 4)
                throw new WrongFormatException();
            else
                this.subchunkSize = subchunkSize;

            if (audioFormat.Length != 2)
                throw new WrongFormatException();
            else
                this.audioFormat = audioFormat;

            if (numChannels.Length != 2)
                throw new WrongFormatException();
            else
                this.numChannels = numChannels;

            if (sampleRate.Length != 4)
                throw new WrongFormatException();
            else
                this.sampleRate = sampleRate;

            if (byteRate.Length != 4)
                throw new WrongFormatException();
            else
                this.byteRate = byteRate;

            if (blockAlign.Length != 2)
                throw new WrongFormatException();
            else
                this.blockAlign = blockAlign;

            if (bitsPerSample.Length != 2)
                throw new WrongFormatException();
            else
                this.bitsPerSample = bitsPerSample;

            this.extraParams = new byte[2];
            this.extraParams[0] = this.extraParams[1] = 0;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            WavFmtSChunk chunk = obj as WavFmtSChunk;
            if (chunk == null)
                return false;

            return ((this.porownajSkladowe(this.subchunkSize, chunk.subchunkSize)) &&
                (this.porownajSkladowe(this.audioFormat, chunk.audioFormat)) &&
                (this.porownajSkladowe(this.numChannels, chunk.numChannels)) &&
                (this.porownajSkladowe(this.sampleRate, chunk.sampleRate)) &&
                (this.porownajSkladowe(this.byteRate, chunk.byteRate)) &&
                (this.porownajSkladowe(this.blockAlign, chunk.blockAlign)) &&
                (this.porownajSkladowe(this.bitsPerSample, chunk.bitsPerSample)));
        }

        public bool Equals(WavFmtSChunk chunk)
        {
            if (chunk == null)
                return false;

            return ((this.porownajSkladowe(this.subchunkSize, chunk.subchunkSize)) &&
                (this.porownajSkladowe(this.audioFormat, chunk.audioFormat)) &&
                (this.porownajSkladowe(this.numChannels, chunk.numChannels)) &&
                (this.porownajSkladowe(this.sampleRate, chunk.sampleRate)) &&
                (this.porownajSkladowe(this.byteRate, chunk.byteRate)) &&
                (this.porownajSkladowe(this.blockAlign, chunk.blockAlign)) &&
                (this.porownajSkladowe(this.bitsPerSample, chunk.bitsPerSample)));
        }

        public override int GetHashCode()
        {
            return (this.subchunkSize.GetHashCode() ^ this.audioFormat.GetHashCode() ^
                this.numChannels.GetHashCode() ^ this.sampleRate.GetHashCode() ^ 
                this.byteRate.GetHashCode() ^ this.blockAlign.GetHashCode() ^ 
                this.bitsPerSample.GetHashCode());
        }

        public static bool operator==(WavFmtSChunk a, WavFmtSChunk b)
        {
            if (Object.ReferenceEquals(a,b))
                return true;
            if ((a == null) || (b == null))
                return false;

            return ((a.porownajSkladowe(a.subchunkSize, b.subchunkSize)) &&
                (a.porownajSkladowe(a.audioFormat, b.audioFormat)) &&
                (a.porownajSkladowe(a.numChannels, b.numChannels)) &&
                (a.porownajSkladowe(a.sampleRate, b.sampleRate)) &&
                (a.porownajSkladowe(a.byteRate, b.byteRate)) &&
                (a.porownajSkladowe(a.blockAlign, b.blockAlign)) &&
                (a.porownajSkladowe(a.bitsPerSample, b.bitsPerSample)));

        }

        public static bool operator !=(WavFmtSChunk a, WavFmtSChunk b)
        {
            return !(a == b);
        }

        public bool canMerge(WavFmtSChunk second)
        {
            return ((this.porownajSkladowe(this.audioFormat, second.audioFormat)) &&
                (this.porownajSkladowe(this.numChannels, second.numChannels)) &&
                (this.porownajSkladowe(this.sampleRate, second.sampleRate)) &&
                (this.porownajSkladowe(this.bitsPerSample, second.bitsPerSample)));
        }

        public void saveFmtChunk(string destinationPath)
        {
            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(destinationPath)))
            {
                writer.Seek(0, SeekOrigin.End);
                writer.Write(this.subchunkId);
                writer.Write(this.subchunkSize);
                writer.Write(this.audioFormat);
                writer.Write(this.numChannels);
                writer.Write(this.sampleRate);
                writer.Write(this.byteRate);
                writer.Write(this.blockAlign);
                writer.Write(this.bitsPerSample);
                writer.Write(this.extraParams);
            }
        }

        public bool porownajSkladowe(byte[] a, byte[] b)
        {
            if (Object.ReferenceEquals(a,b))
                return true;
            if ((a == null) || (b == null))
                return false;
            if (a.Length != b.Length)
                return false;

            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] != b[i]) return false;
            }
            return true;
        }
    }
}
